﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

namespace PSE.Framework.Communication
{
    /// <summary>
    /// Classe base para todos os componentes de comunicação
    /// </summary>
    /// <typeparam name="TConfig">Tipo da configuração do componente</typeparam>
    /// <typeparam name="TOpenParameter">Tipo do parâmetro de abertura do componente</typeparam>
    public abstract class CommunicationComponent<TConfig, TOpenParameter> : ICommunicationComponent
        where TOpenParameter : class
    {
        #region Status
        private CommunicationStatus _status = CommunicationStatus.Created;
        /// <summary>
        /// Status do componente de comunicação
        /// </summary>
        public CommunicationStatus Status
        {
            get { return _status; }
        }
        #endregion

        #region Configuration
        private TConfig _configuration = default(TConfig);
        /// <summary>
        /// Configuração do componente de comunicação
        /// </summary>
        protected TConfig Configuration
        {
            get { this.VerifyIsOpened(); return _configuration; }
        }
        #endregion

        #region Open
        /// <summary>
        /// Abre o componente de comunicação
        /// </summary>
        /// <param name="config">Configuração do componente</param>
        public void Open(TConfig config)
        {
            this.Open(config, null);
        }

        /// <summary>
        /// Abre o componente de comunicação
        /// </summary>
        /// <param name="config">Configuração do componente</param>
        /// <param name="parameter">Parâmetro de abertura do componente</param>
        public virtual void Open(TConfig config, TOpenParameter parameter)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            this.VerifyIsCreated();

            _configuration = config;

            _status = CommunicationStatus.Opened;

            this.OnOpened(new EventArgs());
        }

        /// <summary>
        /// Abre o componente de comunicação
        /// </summary>
        /// <param name="config">Configuração do componente</param>
        void ICommunicationComponent.Open(object config)
        {
            this.Open((TConfig)config);
        }

        /// <summary>
        /// Abre o componente de comunicação
        /// </summary>
        /// <param name="config">Configuração do componente</param>
        /// <param name="parameter">Parâmetros do componente</param>
        void ICommunicationComponent.Open(object config, object parameter)
        {
            this.Open((TConfig)config, (TOpenParameter)parameter);
        }
        #endregion

        #region Opened
        /// <summary>
        /// Evento disparado quando o componente é aberto
        /// </summary>
        public event EventHandler Opened;

        /// <summary>
        /// Dispara evento Opened
        /// </summary>
        /// <param name="e">Argumento do evento</param>
        protected virtual void OnOpened(EventArgs e)
        {
            if (e == null)
                throw new ArgumentNullException("e");

            if (Opened != null)
            {
                Opened(this, e);
            }
        }
        #endregion

        #region Reset
        /// <summary>
        /// Reinicializa o componente de comunicação
        /// </summary>
        protected virtual void Reset()
        {
            // Limpando configuração
            _configuration = default(TConfig);

            // Alterando status
            if (_status == CommunicationStatus.Opened)
                _status = CommunicationStatus.Closed;
        }
        #endregion

        #region Close
        /// <summary>
        /// Fecha o componente de comunicação
        /// </summary>
        public virtual void Close()
        {
            this.VerifyIsOpened();

            this.Reset();

            this.OnClosed(new EventArgs());
        }
        #endregion

        #region Closed
        /// <summary>
        /// Evento disparado quando o componente é fechado
        /// </summary>
        public event EventHandler Closed;

        /// <summary>
        /// Dispara evento Closed
        /// </summary>
        /// <param name="e">Argumento do evento</param>
        protected virtual void OnClosed(EventArgs e)
        {
            if (e == null)
                throw new ArgumentNullException("e");

            if (Closed != null)
            {
                Closed(this, e);
            }
        }
        #endregion

        #region VerifyIsCreated
        /// <summary>
        /// Verifica se o componente foi criado e pode ser aberto
        /// </summary>
        protected virtual void VerifyIsCreated()
        {
            if (this.Status != CommunicationStatus.Created)
            {
                if (this.Status == CommunicationStatus.Opened)
                    throw new InvalidOperationException("Cannot open the communication component, it is already opened");

                if (this.Status == CommunicationStatus.Closed)
                    throw new InvalidOperationException("Cannot open the communication component, it is already closed");
            }
        }
        #endregion

        #region VerifyIsOpened
        /// <summary>
        /// Verifica se o componente está aberto e pode ser acessado
        /// </summary>
        protected virtual void VerifyIsOpened()
        {
            if (this.Status != CommunicationStatus.Opened)
            {
                if (this.Status == CommunicationStatus.Created)
                    throw new InvalidOperationException("Cannot access the communication component, it is not opened yet");

                if (this.Status == CommunicationStatus.Closed)
                    throw new InvalidOperationException("Cannot access the communication component, it is already closed");
            }
        }
        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            if (this.Status == CommunicationStatus.Opened)
            {
                this.Close();
            }
        }

        #endregion
    }
}
